﻿namespace FrequencyAnalysis
{
    using System;
    using System.Drawing;
    using AForge.Imaging.ComplexFilters;
    using AForge.Imaging.Filters;
    using ImageDupFinder.PluginCore;

    /// <summary>
    /// Class representing "frequency" feature. 
    /// This class uses part of Aforge library to perform Fast Fourier Transformation. 
    /// </summary>
    public class Frequency : IFeature
    {
        /// <summary>
        /// feature name
        /// </summary>
        public const string FeatureName = "Frequency";

        /// <summary>
        /// Complex image size (in pixels)
        /// </summary>
        private const int ComplexImgSize = 128;

        /// <summary>
        /// Complex filter size (in pixels)
        /// </summary>
        private const int FilterSize = 18;

        /// <summary>
        /// feature calculated state.
        /// </summary>
        private bool calculated = false;

        /// <summary>
        /// Complex image data length
        /// </summary>
        private int length = 0;

        /// <summary>
        /// Complex image data
        /// </summary>
        private double[] data;

        /// <summary>
        /// Gets or sets data extracted from complex image.
        /// </summary>
        public double[] Data
        {
            get { return this.data; }
            set { this.data = value; }
        }

        /// <summary>
        /// Gets a value indicating whether feature is calculated.
        /// </summary>
        public bool IsCalculated
        {
            get { return this.calculated; }
        }

        /// <summary>
        /// Gets feature name.
        /// </summary>
        public string Name
        {
            get { return FeatureName; }
        }

        /// <summary>
        /// Calculates feature data.
        /// </summary>
        /// <param name="image">image used in feature calculation</param>
        public void CalculateFeature(Bitmap image)
        {
            Bitmap tmp;
            int size = ComplexImgSize - (2 * FilterSize);
            this.length = size * size;

            // reformat image
            AForge.Imaging.Filters.Grayscale gr = new Grayscale(0.2125, 0.7154, 0.0721);
            tmp = gr.Apply(image);

            // scale image
            ResizeNearestNeighbor resize = new ResizeNearestNeighbor(ComplexImgSize, ComplexImgSize);
            tmp = resize.Apply(tmp);

            // load as complex image
            AForge.Imaging.ComplexImage complexImage = AForge.Imaging.ComplexImage.FromBitmap(tmp);

            // do forward Fourier transformation
            complexImage.ForwardFourierTransform();

            // create filter
            FrequencyFilter filter = new FrequencyFilter(new AForge.IntRange(10, 128 - FilterSize));
            double divider = (ComplexImgSize * ComplexImgSize) * 100 / 128;

            // apply filter
            filter.Apply(complexImage);
            //// complexImage.BackwardFourierTransform();
            this.data = new double[this.length];
            for (int i = FilterSize; i < ComplexImgSize - FilterSize; i++)
                for (int j = FilterSize; j < ComplexImgSize - FilterSize; j++)
                {
                    this.data[((i - FilterSize) * size) + (j - FilterSize)] =
                        Double.IsNaN(complexImage.Data[i, j].Phase) ? 0 : complexImage.Data[i, j].Phase / divider;
                }

            tmp.Dispose();

            this.calculated = true;
        }

        /// <summary>
        /// Calculate similarity between two CalculateFeature features.
        /// </summary>
        /// <param name="other">Second feature used in calcualtion</param>
        /// <returns>Similarity in range 0 - 1, where 0 means "identical" and 1 means "completly different"</returns>
        public float GetSimilarity(IFeature other)
        {
            if (!(other is Frequency) || !other.IsCalculated)
            {
                return 1;
            }

            Frequency fr = other as Frequency;
            double val = 0;
            for (int i = 0; i < this.length; i++)
            {
                val += Math.Abs(this.data[i] - fr.Data[i]);
            }

            return (float)val;
        }
    }
}
